package com.lwj.config;

import com.lwj.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;


@Configuration
public class SpringThreadPoolConfig {

    @Value("${mythreadpool.maxPoolSize}")
    private Integer maxPoolSize;
    @Value("${mythreadpool.corePoolSize}")
    private Integer corePoolSize;
    @Value("${mythreadpool.queueCapacity}")
    private Integer queueCapacity;
    @Value("${mythreadpool.keepAliveSeconds}")
    private Integer keepAliveSeconds;
    @Value("${mythreadpool.threadNamePrefix}")
    private String threadNamePrefix;
    @Value("${mythreadpool.waitForTasksToCompleteOnShutdown}")
    private Boolean waitForTasksToCompleteOnShutdown;

    /**
     * ThreadPoolTaskExecutor主要是这四个方法
     * execute()        通过这个方法可以向线程池提交一个任务，交由线程池去执行
     * submit()         通过这个方法也是用来向线程池提交任务的，但是它和execute()方法不同，它能够返回任务执行的结果
     * shutdown()   spring管理不需要执行
     * shutdownNow() spring管理不需要执行
     * @return
     */
    @Bean("asyncTaskExecutor")
    public AsyncTaskExecutor asyncServiceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 当前系统核数
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        // 设置核心线程数
        executor.setCorePoolSize(corePoolSize);
        // 设置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        // 设置线程活跃时间（秒）
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 线程满了之后由调用者所在的线程来执行
        // 拒绝策略：CALLER_RUNS：不在新线程中执行任务，而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置默认线程名称
        executor.setThreadNamePrefix(threadNamePrefix);
        // 等待所有任务结束后再关闭线程池
        // 该方法用来设置 线程池关闭 的时候 等待 所有任务都完成后，再继续 销毁 其他的 Bean，
        // 这样这些 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
        executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown);
        executor.setTaskDecorator(new ContextCopyingDecorator());
        // 线程不够用时由调用的线程处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }

    public static void main(String[] args) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.initialize();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(11);
            }
        });
    }
    /**
     * 当使用ThreadLocal保存数据时，可以把主线程的变量传递给子线程
     */
    class ContextCopyingDecorator implements TaskDecorator {

        @Override
        public Runnable decorate(Runnable runnable) {
            //获取主线程中设置的 user对象
            Object user = ThreadLocalUtil.get(ThreadLocalUtil.USER_KEY).orElse(null);
            return () -> {
                try {
                    //把user对象重新copy传递给子线程
                    if (user != null) {
                        ThreadLocalUtil.set(ThreadLocalUtil.USER_KEY, user);
                    }
                    runnable.run();
                } finally {
                    ThreadLocalUtil.remove();
                }
            };
        }
    }

}
